// record.cpp : record
// 2009-09

#include "record.h"
#include "utils.h"

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <string>

Records g_records;

#define REDIRECT_MODE_1 (0x1)
#define REDIRECT_MODE_2 (0x2)

Records::~Records( void )
{
	size = 0;
	if( NULL != data )
		delete [] data;
	data = NULL;
}

int Records::Load( FILE *fp, const size_t end )
{
	data = new unsigned char[end];
	size_t rc = 0;
	if( fseek( fp, 0, SEEK_SET ) == 0 ) rc = fread( data, end, 1, fp );
	if( 1 != rc ) {
		delete [] data;
		data = NULL;
		return -1;
	}

	size = end;
	return 0;
}

long Records::GetString( std::string &str, size_t start )
{
	if( start >= size )
		return -1;

	int len = 0;
	while( start + len < size && '\0' != data[start + len] )
		len++;

	str.assign( (const char *)&data[start], len );
	return len + 1; // add '\0'
}

int Records::GetLong3( uint32_t &addr, size_t start )
{
	if( start + 3 >= size )
		return -1;
	addr = 0;
	memcpy( (void *)&addr, (void *)&data[start], 3 );
	return 0;
}

int Record::FromAddr( const size_t addr )
{
	if( addr + 8 >= g_records.size )
		return -1;

	// get ip
	memcpy( (void *)&ip, (void *)&g_records.data[addr], 4 );

	// get country and area
	unsigned char byte = g_records.data[addr + 4];
	if( REDIRECT_MODE_1 == byte ) {
		uint32_t new_addr;
		int rc = g_records.GetLong3( new_addr, addr + 5 );
		if( 0 != rc || 0 == new_addr )
			return -1;
		byte = g_records.data[new_addr];
		long offset = 4;
		if( REDIRECT_MODE_2 == byte ) {
			uint32_t t_addr;
			rc = g_records.GetLong3( t_addr, new_addr + 1 );
			if( 0 != rc || 0 == t_addr )
				return -1;
			g_records.GetString( country, t_addr );

		}
		else {
			offset = g_records.GetString( country, new_addr );
		}

		get_area( new_addr + offset );
	}
	else if( REDIRECT_MODE_2 == byte ) {
		uint32_t new_addr;
		int rc = g_records.GetLong3( new_addr, addr + 5 );
		if( 0 != rc || 0 == new_addr )
			return -1;
		g_records.GetString( country, new_addr );
		get_area( addr + 8 );
	}
	else {
		long offset = g_records.GetString( country, addr + 4 );
		get_area( addr + 4 + offset );
	}

	return 0;
}

int Record::get_area( size_t start )
{
	if( start + 1 >= g_records.size )
		return -1;

	unsigned char byte = g_records.data[start];
	if( byte == REDIRECT_MODE_1 || byte == REDIRECT_MODE_2 ) {
		uint32_t addr;
		int rc = g_records.GetLong3( addr, start + 1 );
		if( 0 != rc || 0 == addr ) return -1;
		g_records.GetString( area, addr );
	}
	else {
		g_records.GetString( area, start );
	}

	return 0;
}

